package Entity;

import MObject.DisposeObject;
import World.Module.IdGenerator.IdGenerator;
import World.Module.ObjectPool.ObjectPool;
import com.sun.source.tree.Tree;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

public class Entity extends DisposeObject {

//#if ENABLE_VIEW && UNITY_EDITOR
//        [BsonIgnore]
//            [UnityEngine.HideInInspector]
//    public UnityEngine.GameObject ViewGO;
//#endif

    private long instanceId;

    public long GetInstanceId() {
        return instanceId;
    }

    protected void SetInstanceId(long instanceId) {
        this.instanceId = instanceId;
    }

    protected byte status = EntityStatus.None;


    public boolean GetIsFromPool() {
        return (this.status & EntityStatus.IsFromPool) == EntityStatus.IsFromPool;
    }

    public void SetIsFromPool(boolean isFromPool) {
        if (isFromPool) {
            this.status |= EntityStatus.IsFromPool;
        } else {
            this.status &= ~EntityStatus.IsFromPool;
        }
    }

    public boolean GetIsRegister() {
        return (this.status & EntityStatus.IsRegister) == EntityStatus.IsRegister;
    }

    public void SetIsRegister(boolean isRegister) {
        if (isRegister) {
            this.status |= EntityStatus.IsRegister;
            this.RegisterSystem();
        } else {
            this.status &= ~EntityStatus.IsRegister;
        }
//        #if ENABLE_VIEW && UNITY_EDITOR
//        if (value)
//        {
//            this.ViewGO = new UnityEngine.GameObject(this.ViewName);
//            this.ViewGO.AddComponent<ComponentView>().Component = this;
//            this.ViewGO.transform.SetParent(this.Parent == null?
//                    UnityEngine.GameObject.Find("Global/Scenes").transform : this.Parent.ViewGO.transform);
//        }
//        else
//        {
//            UnityEngine.Object.Destroy(this.ViewGO);
//        }
//        #endif
    }

    public boolean GetIsComponent() {
        return (this.status & EntityStatus.IsComponent) == EntityStatus.IsComponent;
    }

    public void SetIsComponent(boolean isComponent) {
        if (isComponent) {
            this.status |= EntityStatus.IsComponent;
        } else {
            this.status &= ~EntityStatus.IsComponent;
        }
    }

    public boolean GetIsCreate() {
        return (this.status & EntityStatus.IsComponent) == EntityStatus.IsComponent;
    }

    public void SetIsCreate(boolean isCreate) {
        if (isCreate) {
            this.status |= EntityStatus.IsCreated;
        } else {
            this.status &= ~EntityStatus.IsCreated;
        }
    }

    public boolean GetIsNew() {
        return (this.status & EntityStatus.IsNew) == EntityStatus.IsNew;
    }

    public void SetIsIsNew(boolean isNew) {
        if (isNew) {
            this.status |= EntityStatus.IsNew;
        } else {
            this.status &= ~EntityStatus.IsNew;
        }
    }

    private long id;

    public long GetId() {
        return this.id;
    }

    public void SetId(long id) {
        this.id = id;
    }

    private ArrayList<Entity> componentsDB;

    private TreeMap<Long, Entity> components;

    public TreeMap<Long, Entity> GetComponents() {
        if (this.components == null)
            this.components = ObjectPool.<ObjectPool>GetInstance().Fetch(TreeMap.class);
        return this.components;
    }

    public int ComponentCount()
    {
        if(this.components == null)
        {
            return 0;
        }
        return this.components.size();
    }

    protected IScene iScene;

    public IScene GetScene() {
        return this.iScene;
    }

    protected void SetScene(IScene iScene) {
        if (iScene == null) {
            throw new RuntimeException("domain can't set null:" + this.getClass().getName());
        }
        if (this.iScene == iScene) {
            return;
        }
        IScene preScene = this.iScene;

        this.iScene = iScene;

        if (preScene == null) {
            if (this.GetInstanceId() == 0) {
                this.SetInstanceId(IdGenerator.<IdGenerator>GetInstance().GenerateInstanceId());
            }
        }

        this.SetIsRegister(true);

        if (this.componentsDB != null) {
            for (Entity component : this.componentsDB) {
                component.SetIsComponent(true);
                this.GetComponents().put(this.GetLongHashCode(component.getClass()), component);
                component.parent = this;
            }
        }

        if(this.childrenDB != null)
        {
            for(Entity child : childrenDB)
            {
                child.SetIsComponent(false);
                this.GetChildren().put(child.GetId(), child);
                child.parent = this;
            }
        }

        if (children != null)
        {
            for (Entity child : this.children.values())
            {
                child.SetScene(this.iScene);
            }
        }

        if(this.components != null)
        {
            for (Entity component : this.components.values())
            {
                component.SetScene(this.iScene);
            }
        }

        if(!this.GetIsCreate())
        {
            this.SetIsCreate(true);
        }
    }

    private ArrayList<Entity> childrenDB;

    private TreeMap<Long, Entity> children;

    private TreeMap<Long, Entity> GetChildren()
    {
        if(this.children == null)
            this.children = ObjectPool.<ObjectPool>GetInstance().Fetch(TreeMap.class);
        return this.children;
    }

    protected long GetLongHashCode(Class<?> type)
    {
        return type.hashCode();
    }

    public boolean GetIsDisposed() {
        return this.GetInstanceId() == 0;
    }


    private Entity parent;

    public Entity GetParent() {
        return this.parent;
    }

    protected void SetParent(Entity entity) {
        if (entity == null) {
            throw new RuntimeException("Can't set parent with null:" + this.getClass().getName());
        }

        if (entity == this) {
            throw new RuntimeException("Can't set parent with self:" + this.getClass().getName());
        }

    }


    protected Entity() {

    }

    public void RegisterSystem() {

    }


}
